
package com.fl.util;

import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

/**
 * 
 * <pre>
 * 日期公共处理类<BR>
 * 
 * <li>预定义常量设置</li> <BR>
 * <li>常量:DATE_FMT_Y_M_D_H_M_S 格式:yyyy-MM-dd HH:mm:ss</li>
 * <li>常量:DATE_FMT_YMDHMSS     格式:yyyyMMddHHmmssSSS</li>
 * <li>常量:DATE_FMT_YMDHMSS_CHS 格式:yyyy年MM月dd日HH点mm分ss秒SSS毫秒</li>
 * <li>常量:DATE_FMT_YMDHMS      格式:yyyyMMddHHmmss</li>
 * <li>常量:DATE_FMT_YMDHMS_CHS  格式:yyyy年MM月dd日HH点mm分ss秒</li>
 * <li>常量:DATE_FMT_Y_M_D       格式:yyyy-MM-dd</li>
 * <li>常量:DATE_FMT_YMD         格式:yyyyMMdd</li>
 * <li>常量:DATE_FMT_YMD_CHS     格式:yyyy年MM月dd日</li>
 * <li>常量:DATE_FMT_YM          格式:yyyyMM</li>
 * <li>常量:DATE_FMT_YM_CHS      格式:yyyy年MM月</li>
 * <li>常量:DATE_FMT_Y           格式:yyyy</li>
 * <li>常量:DATE_FMT_Y_CHS       格式:yyyy年</li>
 * <li>常量:DATE_FMT_HMS         格式:HHmmss</li>
 * <li>常量:DATE_FMT_HMS_CHS     格式:HH点mm分ss秒</li>
 * <li>常量:DATE_FMT_MD          格式:MMdd</li>
 * <li>常量:DATE_FMT_MD_CHS      格式:MM月dd日</li>
 * <li>常量:DATE_FMT_HM          格式:HH:mm</li>
 * 
 * <BR>
 * <li>功能方法</li> 
 * <li>日期合法校验</li> 
 * <li>日期字符串指定格式换转</li>
 * <li>时间戳字符串指定格式换转</li> 
 * <li>日期相关字段增减操作</li> 
 * <li>两个日期指定单位差值计算</li> 
 * <li>得到日期所在星期第一天</li> 
 * <li>得到日期所在月第一天</li> 
 * <li>得到日期所在季度第一天</li>
 * 
 * </pre>
 * 
 * @author liangli
 * */
public class DateUtil {

    // 预定义日期格式常量
    public static final String DATE_FMT_Y_M_D_H_M_S = "yyyy-MM-dd HH:mm:ss";
    public static final String DATE_FMT_YMDHMSS     = "yyyyMMddHHmmssSSS";
    public static final String DATE_FMT_YMDHMSS_CHS = "yyyy年MM月dd日HH点mm分ss秒SSS毫秒 ";
    public static final String DATE_FMT_YMDHMS      = "yyyyMMddHHmmss";
    public static final String DATE_FMT_YMDHMS_CHS  = "yyyy年MM月dd日HH点mm分ss秒";
    public static final String DATE_FMT_Y_M_D       = "yyyy-MM-dd";
    public static final String DATE_FMT_YMD         = "yyyyMMdd";
    public static final String DATE_FMT_YMD_CHS     = "yyyy年MM月dd日";
    public static final String DATE_FMT_YM          = "yyyyMM";
    public static final String DATE_FMT_YM_CHS      = "yyyy年MM月";
    public static final String DATE_FMT_Y           = "yyyy";
    public static final String DATE_FMT_Y_CHS       = "yyyy年";
    public static final String DATE_FMT_HMS         = "HHmmss";
    public static final String DATE_FMT_HMS_CHS     = "HH点mm分ss秒";
    public static final String DATE_FMT_MD          = "MMdd";
    public static final String DATE_FMT_MD_CHS      = "MM月dd日";
    public static final String DATE_FMT_HM          = "HH:mm";

    /**
     * 
     * 方法名 ：isValidDate<BR>
     * 方法说明 ：判断是日期是否合法<BR>
     * 备注：date和format任一为空（null,空串,多个空格字符）返回false<BR>
     * 
     * 
     * @param date
     *            要校验的日期字符串<BR>
     * @param format
     *            日期格式<BR>
     * @return 是否合法<BR>
     */
    public static boolean isValidDate(String date, String format) {

        // 参数校验
        if (date == null || "".equals(date.trim()) || format == null || "".equals(format.trim())) {
            return false;
        }

        // format实例
        SimpleDateFormat s = new SimpleDateFormat(format);
        try {
            // 严格校验
            s.setLenient(false);
            // 转换日期
            s.parse(date);
            return true;
        } catch (Exception e) {
            // 任何错误都是异常格式
            return false;
        }
    }

    /**
     * 方法名 ：parseDate<BR>
     * 方法说明 ：把字符串按照给定格式进行格式化<BR>
     * 备注 ：<BR>
     * 1、date和format任一为空（null,空串,多个空格字符）抛出异常<BR>
     * 2、format可以使用内置预定义的格式<BR>
     * 
     * @param date
     *            要格式化的日期字符串<BR>
     * @param format
     *            日期格式<BR>
     * @return 格式化的日期对象<BR>
     * @throws ParseException
     * <BR>
     */
    public static Date parseDate(String date, String format) throws ParseException {

        // 参数校验
        if (date == null || "".equals(date.trim()) || format == null || "".equals(format.trim())) {
            throw new IllegalArgumentException("Args can not be null!");
        }

        return new SimpleDateFormat(format).parse(date);
    }

    /**
     * 方法名 ：formatDate<BR>
     * 方法说明 ：把日期对象按照给定格式进行格式化<BR>
     * 备注 ：<BR>
     * 1、date和format任一为空（null,空串,多个空格字符）抛出异常<BR>
     * 2、format可以使用内置预定义的格式<BR>
     * 
     * @param date
     *            要格式化的日期对象<BR>
     * @param format
     *            日期格式<BR>
     * @return 格式化的字符串<BR>
     */
    public static String formatDate(Date date, String format) {

        // 参数校验
        if (date == null || format == null || "".equals(format.trim())) {
            throw new IllegalArgumentException("Args can not be null!");
        }

        return new SimpleDateFormat(format).format(date);
    }

    /**
     * 方法名 ：transDateFormat<BR>
     * 方法说明 ：把字符串从一种日期格式转化成另外一种日期格式<BR>
     * 备注 ：<BR>
     * 1、date,format1,format2任一为空（null,空串,多个空格字符）抛出异常<BR>
     * 2、日期格式可以使用内置预定义的格式 <BR>
     * 
     * @param date
     *            要格式化的日期对象<BR>
     * @param format1
     *            原日期格式<BR>
     * @param format2
     *            目标日期格式<BR>
     * @return 格式化的字符串<BR>
     */
    public static String transDateFormat(String date, String format1, String format2) throws ParseException {
        return formatDate(parseDate(date, format1), format2);
    }

    /**
     * 方法名 ：parseTimestamp<BR>
     * 方法说明 ：把字符串按照给定格式进行格式化<BR>
     * 备注 ：<BR>
     * 1、date和format任一为空（null,空串,多个空格字符）抛出异常<BR>
     * 2、format可以使用内置预定义的格式<BR>
     * 
     * @param time
     *            要格式化的时间戳字符串<BR>
     * @param format
     *            时间戳格式<BR>
     * @return 格式化的时间戳对象<BR>
     * @throws ParseException
     * <BR>
     */
    public static Timestamp parseTimestamp(String time, String format) throws ParseException {
        return new Timestamp(parseDate(time, format).getTime());
    }

    /**
     * 方法名 ：formatTimestamp<BR>
     * 方法说明 ：把时间戳对象按照给定格式进行格式化<BR>
     * 备注 ：<BR>
     * 1、date和format任一为空（null,空串,多个空格字符）抛出异常<BR>
     * 2、format可以使用内置预定义的格式<BR>
     * 
     * @param time
     *            要格式化的时间戳对象<BR>
     * @param format
     *            时间戳格式<BR>
     * @return 格式化的字符串<BR>
     */
    public static String formatTimestamp(Timestamp time, String format) {

        // 参数校验
        if (time == null || format == null || "".equals(format.trim())) {
            throw new IllegalArgumentException("Args can not be null!");
        }

        return new SimpleDateFormat(format).format(time);
    }

    /**
     * 方法名 ：compute<BR>
     * 方法说明 ：计算两个时间之间的差值,单位通过unit定义<BR>
     * 备注 ：<BR>
     * 1、date1,date2,unit任一为空（null,空串,多个空格字符）抛出异常<BR>
     * 2、unit单位有周WEEK,日DAY,小时HOUR,分MINUTE,秒SECOND<BR>
     * 
     * @param date1
     *            时间1<BR>
     * @param date2
     *            时间2<BR>
     * @param unit
     *            差值单位<BR>
     * @return 时间差值<BR>
     */
    public static long compute(Date date1, Date date2, DateEnum unit) {

        // 校验参数
        if (date1 == null || date2 == null || unit == null) {
            throw new IllegalArgumentException("Args can not be null!");
        }

        // 得到时间差,单位毫秒
        long delta = date2.getTime() - date1.getTime();

        // 根据类型返回
        switch (unit) {
            case WEEK:
                return delta / (7 * 24 * 60 * 60 * 1000);
            case DAY:
                return delta / (24 * 60 * 60 * 1000);
            case HOUR:
                return delta / (60 * 60 * 1000);
            case MINUTE:
                return delta / (60 * 1000);
            case SECOND:
                return delta / 1000;
            default:
                throw new IllegalArgumentException("Arg is not valid!");
        }
    }

    /**
     * 方法名 ：modify<BR>
     * 方法说明 ：对日期对象进行修改<BR>
     * 备注 ：<BR>
     * 1、date为空（null）抛出异常 <BR>
     * 2、year,month,day,hour,minute,second都可以使用正负数来增加或者减少对应单位<BR>
     * 
     * @param year
     *            要增加或者减少的年(零表示不修改,负数表示减少)<BR>
     * @param month
     *            要增加或者减少的月(零表示不修改,负数表示减少)<BR>
     * @param day
     *            要增加或者减少的日(零表示不修改,负数表示减少)<BR>
     * @param hour
     *            要增加或者减少的时(零表示不修改,负数表示减少)<BR>
     * @param minute
     *            要增加或者减少的分(零表示不修改,负数表示减少)<BR>
     * @param second
     *            要增加或者减少的秒(零表示不修改,负数表示减少)<BR>
     * @return 修改好的日期对象<BR>
     */
    public static Date modify(Date date, int year, int month, int day, int hour, int minute, int second) {

        // 校验参数
        if (date == null) {
            throw new IllegalArgumentException("Args can not be null!");
        }

        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.add(Calendar.YEAR, year);
        c.add(Calendar.MONTH, month);
        c.add(Calendar.DAY_OF_MONTH, day);
        c.add(Calendar.HOUR_OF_DAY, hour);
        c.add(Calendar.MINUTE, minute);
        c.add(Calendar.SECOND, second);

        return c.getTime();
    }

    /**
     * 方法名 ：getWeeklyStartCHS<BR>
     * 方法说明 ：得到当前日期所在周的第一天<BR>
     * 备注 ：<BR>
     * 1、date为空抛出异常<BR>
     * 2、按照中国习惯每周的周一为第一天<BR>
     * 
     * @param date
     *            要计算的日期对象<BR>
     * @return 本周第一天日期对象<BR>
     */
    public static Date getWeeklyStartCHS(Date date) {
        return getWeeklyStart(date, DateEnum.MONDAY);
    }

    /**
     * 方法名 ：getWeeklyStartUS<BR>
     * 方法说明 ：得到当前日期所在周的第一天<BR>
     * 备注 ：<BR>
     * 1、date为空抛出异常 <BR>
     * 2、按照西方习惯每周的周日为第一天<BR>
     * 
     * @param date
     *            要计算的日期对象<BR>
     * @return 本周第一天日期对象<BR>
     */
    public static Date getWeeklyStartUS(Date date) {
        return getWeeklyStart(date, DateEnum.SUNDAY);
    }

    private static Date getWeeklyStart(Date date, DateEnum firstDay) {

        // 校验参数
        if (date == null || firstDay == null) {
            throw new IllegalArgumentException("Args can not be null!");
        }

        Calendar c = Calendar.getInstance();

        // 根据类型返回
        switch (firstDay) {
            // 每周日为每周的第一天
            case SUNDAY:
                c.setFirstDayOfWeek(Calendar.SUNDAY);
                c.setTime(date);
                c.set(Calendar.DAY_OF_WEEK, c.getFirstDayOfWeek());
                return c.getTime();
            // 每周一位每周的第一天
            case MONDAY:
                c.setFirstDayOfWeek(Calendar.MONDAY);
                c.setTime(date);
                c.set(Calendar.DAY_OF_WEEK, c.getFirstDayOfWeek());
                return c.getTime();
            default:
                throw new IllegalArgumentException("Arg is not valid!");
        }
    }

    /**
     * 方法名 ：getMonthlyStart<BR>
     * 方法说明 ：得到当前日期所在月的第一天<BR>
     * 备注 ：date为空抛出异常 <BR>
     * 
     * @param date
     *            要计算的日期对象<BR>
     * @return 本月第一天日期对象<BR>
     */
    public static Date getMonthlyStart(Date date) {

        // 校验参数
        if (date == null) {
            throw new IllegalArgumentException("Args can not be null!");
        }

        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.set(Calendar.DAY_OF_MONTH, 1);
        return c.getTime();
    }

    /**
     * 方法名 ：getQuarterlyStart<BR>
     * 方法说明 ：得到当前日期所在季度的第一天<BR>
     * 备注 ：date为空抛出异常 <BR>
     * 
     * @param date
     *            要计算的日期对象<BR>
     * @return 本季度第一天日期对象<BR>
     */
    public static Date getQuarterlyStart(Date date) {

        // 校验参数
        if (date == null) {
            throw new IllegalArgumentException("Args can not be null!");
        }

        Calendar c = Calendar.getInstance();
        c.setTime(date);

        // Calendar月份从0开始
        int cureMonth = c.get(Calendar.MONTH) + 1;
        int startMon = 0;

        // 从当前月度判断季度第一个月
        if (1 <= cureMonth && cureMonth <= 3) {
            startMon = 1;
        } else if (4 <= cureMonth && cureMonth <= 6) {
            startMon = 4;
        } else if (7 <= cureMonth && cureMonth <= 9) {
            startMon = 7;
        } else if (10 <= cureMonth && cureMonth <= 12) {
            startMon = 10;
        }

        // 设置起始的日期
        c.set(Calendar.MONTH, startMon - 1);
        c.set(Calendar.DAY_OF_MONTH, 1);

        return c.getTime();
    }
}
